package org.hackystat.ui.projectviewer.gwt.client;

import org.hackystat.ui.projectviewer.gwt.client.common.MPListHolder;
import org.hackystat.ui.projectviewer.gwt.client.common.ProjectViewerException;
import org.hackystat.ui.projectviewer.gwt.client.common.Session;
import org.hackystat.ui.projectviewer.gwt.client.common.MainPanelWidget.MPHandler;
import org.hackystat.ui.projectviewer.gwt.client.common.types.ProjectData;
import org.hackystat.ui.projectviewer.gwt.client.controller.ProjectViewerController;
import org.hackystat.ui.projectviewer.gwt.client.dataprovider.DataProviderMediator;
import org.hackystat.ui.projectviewer.gwt.client.panels.AboutPanel;
import org.hackystat.ui.projectviewer.gwt.client.panels.DailyDairyPanel;
import org.hackystat.ui.projectviewer.gwt.client.panels.EditProjectPanel;
import org.hackystat.ui.projectviewer.gwt.client.panels.ProjectChartPanel;
import org.hackystat.ui.projectviewer.gwt.client.panels.ProjectDataPanel;
import org.hackystat.ui.projectviewer.gwt.client.panels.ProjectInfoPanel;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 *
 * @author Pavel Senin.
 */
public class ProjectViewer implements EntryPoint, HistoryListener {

  /* the main ProjectViewer panel handler. */
  private static ProjectViewer singleton;

  /* the list of "main" panels that could be displayed. */
  protected MPListHolder list = new MPListHolder();
  private String curHandler;

  /** Settings pane wrapper. */
  final VerticalPanel settingsOuter = new VerticalPanel();
  /** The hosts and user login panel widget. */
  final ConfigurationWidgetPanel loginPane = new ConfigurationWidgetPanel(this);
  /** This session data keeper. */
  final Session session = new Session();

  private static final String title = "<div style=\"color: white; font-weight: strong;\">"
      + "ProjectViewer Original </div>";

  // the main panel
  private VerticalPanel managementPanel = new VerticalPanel();

  private static final String infoPanel = "projectInfo";
  private static final String dataPanel = "projectData";
  private static final String chartPanel = "projectChart";
  private static final String dailyDairyPanel = "dailyDairy";

  // logger additions
  // private static final int[] levels = { Log.LOG_LEVEL_DEBUG, Log.LOG_LEVEL_INFO,
  // Log.LOG_LEVEL_WARN, Log.LOG_LEVEL_ERROR, Log.LOG_LEVEL_FATAL, Log.LOG_LEVEL_OFF, };
  //
  // private static final String[] levelTexts = { "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF",
  // };

  // needed for time measurements
  private long startTimeMillis;

  /**
   * Gets the singleton instance.
   *
   * @return the projectViewer singleton instance.
   */
  public static ProjectViewer get() {
    return singleton;
  }

  /**
   * Main entry point method.
   */
  public void onModuleLoad() {
    // set uncaught exception handler
    Log.setUncaughtExceptionHandler();

    // use a deferred command so that the handler catches onModuleLoad2()
    // exceptions
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        onModuleLoad2();
      }
    });
  }

  /**
   * Deferred initialization method, used by {@link #onModuleLoad()}.
   */
  private void onModuleLoad2() {

    if (Log.isDebugEnabled()) {
      startTimeMillis = System.currentTimeMillis();
    }

    singleton = this;

    // add title to the page
    //
    RootPanel.get("text").add(new HTML(title));
    // add hosts and login panel to the page
    //
    settingsOuter.add(loginPane);
    RootPanel.get("settings").add(settingsOuter);
    // and finish make-up by adding "manager" panel
    //
    managementPanel.setStyleName(CSSStyles.MANAGEMENT_PANEL_STYLE);
    RootPanel.get("manager").add(managementPanel);

    // load all the panels now
    //
    loadPanels();

    History.addHistoryListener(this);

    //
    // now get the RPC data provider instance
    // service = DataProviderMediator.getInstance();
    String initToken = History.getToken();
    if (initToken.length() > 0) {
      onHistoryChanged(initToken);
    }
    else {
      showInfo();
    }

    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: This is a 'DEBUG' window (panel) and it is draggable");
    }

    if (Log.isDebugEnabled()) {
      long endTimeMillis = System.currentTimeMillis();
      float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
      Log.debug("PV-MAIN: App loaded in : " + durationSeconds + " seconds");
    }

  }

  /**
   * Shows the aboutPanel, used in the app load.
   */
  private void showInfo() {
    show("aboutPanel", false);
  }

  /**
   * Reports a RPC service mediators.
   *
   * @return The RPC service mediator.
   */
  public DataProviderMediator getService() {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: reporting DataProviderMediator.getInstance()");
    }
    return DataProviderMediator.getInstance();
  }

  /**
   * Sets selection of project on the Project information panel.
   *
   * @param projectId The project name.
   */
  public void setSelection(String projectId) {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: got put ProjectInfoPanel + " + projectId + " on show");
    }
    ProjectInfoPanel pif = (ProjectInfoPanel) list.find(infoPanel).getInstance();
    pif.setProject(projectId);
    show(infoPanel, false);
  }

  /**
   * Shows the stipulated panel.
   *
   * @param panelName the panel name to show.
   * @param affectHistory whether or not this move affects the history.
   */
  public void show(String panelName, boolean affectHistory) {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: " + panelName + " goes on show. Affects history: " + affectHistory);
    }
    if ((curHandler != null) && (panelName.equalsIgnoreCase(curHandler))) {
      return;
    }
    // Remove the old sink from the display area.
    if (curHandler != null) {
      MPHandler handler = list.find(curHandler);
      handler.onHide();
      managementPanel.remove(handler.getInstance());
    }
    // Get the new sink instance, and display its description in the
    // sink list.
    curHandler = panelName;
    // If affectHistory is set, create a new item on the history stack. This
    // will ultimately result in onHistoryChanged() being called. It will call
    // show() again, but nothing will happen because it will request the exact
    // same sink we're already showing.
    if (affectHistory) {
      History.newItem(panelName);
    }
    // Display the new sink.
    MPHandler handler = list.find(curHandler);
    handler.onShow();
    managementPanel.add(handler.getInstance());
  }

  /**
   * Adds all other panels to the list. Note that this DOES create actual instances of all panels
   * (they are NOT created on-demand). This can make a significant difference in startup time.
   */
  protected void loadPanels() {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: initializing panels");
    }
    list.addPanel(AboutPanel.init(this));
    list.addPanel(ProjectInfoPanel.init());
    list.addPanel(EditProjectPanel.init(this));
    list.addPanel(ProjectDataPanel.init());
    list.addPanel(ProjectChartPanel.init());
    list.addPanel(DailyDairyPanel.init());
  }

  /**
   * {@inheritDoc}
   */
  public void onHistoryChanged(String historyToken) {
    // nothing to do so far in here
    assert true;
  }

  /**
   * Puts project data to sensorbase.
   *
   * @param success The project data to PUT.
   */
  public void updateProject(final ProjectData success) {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: doing RPC to update project " + success.getName());
    }
    AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
      // If the call was successful, we will get back the result of authorization.
      public void onSuccess(Boolean saved) {
        DataProviderMediator.getInstance().unlock();
        if (Log.isDebugEnabled()) {
          long endTimeMillis = System.currentTimeMillis();
          float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
          Log.debug("PV-MAIN: unlocked. success in : " + durationSeconds + " seconds");
        }
        if (saved) {
          // ok, updated project data received - let's rock
          loginPane.refreshLists();
          setSelection(success.getName());
        }
        else {
          // --><><>< lock RPC and display some clues about why we unable to update project
          if (DataProviderMediator.getInstance().lock()) {
            DataProviderMediator.getInstance().getException(new AsyncCallback<String>() {
              public void onSuccess(String result) {
                DataProviderMediator.getInstance().unlock();
                Window.alert("Unable to update/save project, please provide feedback "
                    + "to hackystat-dev@googlegroups.com. \n Error: " + result);
              }

              public void onFailure(Throwable ex) {
                DataProviderMediator.getInstance().unlock();
                Window.alert("Unknown exception happend during RPC call. \n Please provide "
                    + "feedback to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
              }
            });
          } // --><><>< end of error retrieving code.
        }
      }

      // Something went wrong with the call. Handle the issue how you'd like.
      public void onFailure(Throwable ex) {
        DataProviderMediator.getInstance().unlock();
        if (Log.isDebugEnabled()) {
          long endTimeMillis = System.currentTimeMillis();
          float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
          Log.debug("PV-MAIN: unlocked. failure in : " + durationSeconds + " seconds");
        }
        Window.alert("Unknown exception happend during RPC call. \n Please provide feedback "
            + "to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
      }
    };
    if (DataProviderMediator.getInstance().lock()) {
      if (Log.isDebugEnabled()) {
        startTimeMillis = System.currentTimeMillis();
        Log.debug("PV-MAIN: DataProviderMediator locked. ");
      }
      DataProviderMediator.getInstance().saveProjectData(success, callback);
    }
    else {
      Window.alert("RPC is locked, unable to proceed with communications: "
          + "DataProviderMediator is locked. \n Please provide "
          + "feedback to hackystat-dev@googlegroups.com.");
    }
  }

  /**
   * Cancel project editing and displays little about panel.
   *
   * @param string The project we were editing.
   */
  public void cancelEditProject(String string) {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: changes canceled. Refreshing list of projects.");
    }
    loginPane.refreshLists();
    show("aboutPanel", false);
  }

  /**
   * Allows project creation or editing.
   *
   * @param selection The project name.
   * @param mode The mode: true - creation of the new project, false - editing of the project.
   */
  public void editProject(String selection, Boolean mode) {
    if (mode) {
      if (Log.isDebugEnabled()) {
        Log.debug("PV-MAIN: creating NEW project");
      }
      try {
        ProjectData pd = new ProjectData();
        pd.setOwner(Session.getInstance().getUser());
        pd.setName("changeme!");
        if (Log.isDebugEnabled()) {
          Log.debug("PV-MAIN: new ProjectData instance created");
        }
        ((EditProjectPanel) list.find("editProject").getInstance()).setModeNew();
        ((EditProjectPanel) list.find("editProject").getInstance()).setProjectData(pd);
        show("editProject", false);
      }
      catch (ProjectViewerException e) {
        e.printStackTrace();
      }
    }
    else {
      if (Log.isDebugEnabled()) {
        Log.debug("PV-MAIN: about to edit " + selection);
      }
      AsyncCallback<ProjectData> callback = new AsyncCallback<ProjectData>() {
        // If the call was successful, we will get back the result of authorization.
        public void onSuccess(ProjectData s) {
          DataProviderMediator.getInstance().unlock();
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PV-MAIN: unlocked. retrieved data for " + s.getName() + " in "
                + durationSeconds + " seconds");
          }
          if (s == null) {
            // --><><>< lock RPC and display some clues about why we unable to get project data
            if (DataProviderMediator.getInstance().lock()) {
              DataProviderMediator.getInstance().getException(new AsyncCallback<String>() {
                public void onSuccess(String result) {
                  DataProviderMediator.getInstance().unlock();
                  Window.alert("Unable to get project data, please provide feedback "
                      + "to hackystat-dev@googlegroups.com. \n Error: " + result);
                }

                public void onFailure(Throwable ex) {
                  DataProviderMediator.getInstance().unlock();
                  Window.alert("Unknown exception happend during RPC call. \n Please provide "
                      + "feedback to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
                }
              });
            } // --><><>< end of error retrieving code.
          }
          else {
            // ok, updated project data received - let's rock
            ((EditProjectPanel) list.find("editProject").getInstance()).setModeEdit();
            ((EditProjectPanel) list.find("editProject").getInstance()).setProjectData(s);
            show("editProject", false);
          }
        }

        // Something went wrong with the call. Handle the issue how you'd like.
        public void onFailure(Throwable ex) {
          DataProviderMediator.getInstance().unlock();
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PV-MAIN: unlocked. failure in : " + durationSeconds + " seconds");
          }
          Window.alert("Unknown exception happend during RPC call. \n Please provide "
              + "feedback to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
        }
      };
      if (DataProviderMediator.getInstance().lock()) {
        if (Log.isDebugEnabled()) {
          startTimeMillis = System.currentTimeMillis();
          Log.debug("PV-MAIN: locked. getting project data for " + selection);
        }
        DataProviderMediator.getInstance().getProjectData(selection, callback);
      }
      else {
        Window.alert("RPC is locked, unable to proceed with communications. \n Please provide "
            + "feedback to hackystat-dev@googlegroups.com.");
      }
    }
  }

  /**
   * Refreshes all panels.
   */
  public void refresh() {
    loginPane.refreshLists();
    show("aboutPanel", false);
  }

  /**
   * Deletes the project.
   *
   * @param selection The project name.
   */
  public void deleteProject(String selection) {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: deleting the project " + selection);
    }
    AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
      // If the call was successful, we will get back the result of authorization.
      public void onSuccess(Boolean deleted) {
        DataProviderMediator.getInstance().unlock();
        if (deleted) {
          if (Log.isDebugEnabled()) {
            long endTimeMillis = System.currentTimeMillis();
            float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
            Log.debug("PV-MAIN: unlocked. Delete success in : " + durationSeconds + " seconds");
          }
          ProjectViewerController.getInstance().resetMemory();
          refresh();
        }
        else {
          // --><><>< lock RPC and display some clues about why we unable to delete project
          if (DataProviderMediator.getInstance().lock()) {
            DataProviderMediator.getInstance().getException(new AsyncCallback<String>() {
              public void onSuccess(String result) {
                DataProviderMediator.getInstance().unlock();
                GWT.log("throwing delete exception", null);
                Window.alert("Unable to delete project, please provide feedback "
                    + "to hackystat-dev@googlegroups.com. \n Error: " + result);
              }

              public void onFailure(Throwable ex) {
                DataProviderMediator.getInstance().unlock();
                Window.alert("Unknown exception happend during RPC call. \n Please provide "
                    + "feedback to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
              }
            });
          } // --><><>< end of error retrieving code.
        }
      }

      // Something went wrong with the call. Handle the issue how you'd like.
      public void onFailure(Throwable ex) {
        DataProviderMediator.getInstance().unlock();
        if (Log.isDebugEnabled()) {
          long endTimeMillis = System.currentTimeMillis();
          float durationSeconds = (endTimeMillis - startTimeMillis) / 1000F;
          Log.debug("PV-MAIN: unlocked. Delete failure in : " + durationSeconds + " seconds");
        }
        Window.alert("Unknown exception happend during RPC call. \n Please provide "
            + "feedback to hackystat-dev@googlegroups.com. \n Error: " + ex.getMessage());
      }
    };
    if (DataProviderMediator.getInstance().lock()) {
      if (Log.isDebugEnabled()) {
        startTimeMillis = System.currentTimeMillis();
        Log.debug("PV-MAIN: locked. deleting project " + selection);
      }
      DataProviderMediator.getInstance().deleteProject(selection, callback);
    }
    else {
      Window.alert("RPC is locked, unable to proceed with communications. \n Please provide "
          + "feedback to hackystat-dev@googlegroups.com.");
    }

  }

  /**
   * Resets buttons.
   */
  public void resetButtons() {
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: refreshing buttons on login pane");
    }
    loginPane.resetButtons();
  }

  /**
   * Puts data download panel on show and allows data retrieval.
   *
   * @param projectSelection The project to work with.
   */
  public void startDataRetrieval(String projectSelection) {
    ProjectDataPanel pdp = (ProjectDataPanel) list.find(dataPanel).getInstance();
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: setting " + projectSelection + "to project data panel.");
    }
    pdp.setProject(projectSelection);
    show(dataPanel, false);
  }

  /**
   * Puts projects chart panel on show.
   *
   * @param projectSelection The project to work with.
   */
  public void startChartRendering(String projectSelection) {
    ProjectChartPanel pcp = (ProjectChartPanel) list.find(chartPanel).getInstance();
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: setting " + projectSelection + "to project chart panel.");
    }
    pcp.setProject(projectSelection);
    show(chartPanel, false);
  }

  /**
   * Puts daily dairy panel on show.
   *
   * @param projectSelection The project to work with.
   */
  public void startProjectDiary(String projectSelection) {
    DailyDairyPanel pdd = (DailyDairyPanel) list.find(chartPanel).getInstance();
    if (Log.isDebugEnabled()) {
      Log.debug("PV-MAIN: setting " + projectSelection + " to daily dairy panel.");
    }
    pdd.setProject(projectSelection);
    show(dailyDairyPanel, false);
  }


}
